﻿
using Common;
using InstallWizard.Model;
using Ionic.Zip;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;

using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Linq;

namespace InstallWizardMaker
{
    public partial class FrmMaker : Form
    {
        BindingList<AppFileInfo> bFiles = new BindingList<AppFileInfo>();
        string configFile = "wizard.config";
        string wizardPath = @"D:\Dev\Gitee\InstallWizardMaker\InstallWizard\bin\Debug";
        string pkgFilename = "files.pkg";
        
        MyConfiguration hisConf ;//用来记忆最近一次使用过的路径
        public FrmMaker()
        {
            InitializeComponent();
        }

        private void FrmMaker_Load(object sender, EventArgs e)
        {
            wizardPath = Path.Combine(Environment.CurrentDirectory, "Installer");
            dgvData.AutoGenerateColumns = false;
            dgvData.DataSource = bFiles;
            if (!File.Exists("current.dat")) File.WriteAllText("current.dat", "");//如果还没有记忆文件，就产生一个空的记忆文件
            hisConf = new MyConfiguration("current.dat",false);
            if (hisConf.ContainsKey(hisLicFile)) txtLicense.Text = hisConf.ReadString(hisLicFile);
            if (hisConf.ContainsKey(logoFile)) txtAppLogo.Text = hisConf.ReadString(logoFile);
            if (hisConf.ContainsKey(clrFile)) txtCLR.Text = hisConf.ReadString(clrFile);
        }
        string rootDir = "";

        string srcDir = "SrcDir";//源文件目录
        private void BtnAddFiles_Click(object sender, EventArgs e)
        {
            string currDir = null;
            if (hisConf.ContainsKey(srcDir))
            {
                currDir = hisConf.ReadString(srcDir);
            }
            FolderBrowserDialog fd = new FolderBrowserDialog()
            {
                Description = "请指定要添加的软件包中的源路径。该路径下所有文件（含子路径下的）将会添加到清单。" +
                "\r\n你可以多次添加源路径。首次添加的源将是软件的主目录。\r\n如有必要，你可以编辑这个清单",
                SelectedPath = currDir 
            };
            if(fd.ShowDialog()==DialogResult.OK)
            {
                hisConf.WriteString(srcDir, fd.SelectedPath);
                if (string.IsNullOrEmpty(rootDir)) rootDir = fd.SelectedPath;//
                AddPathRecursive(fd.SelectedPath, rootDir);
                var exeFiles = bFiles.Where(f => f.SourceFile.EndsWith(".exe", StringComparison.OrdinalIgnoreCase));
                if(exeFiles.Count()>=1)
                {
                    exeFiles.First().FileType = EFileType.MainProgram;
                    MessageBox.Show("你添加了多个可执行文件，请将其中之一指定为主程序","提示",MessageBoxButtons.OK,MessageBoxIcon.Information);
                }
                else 
                {
                    MessageBox.Show("你尚未添加主程序，请继续添加文件", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                dgvData.Refresh();
            }
        }
        /// <summary>
        /// 递归加入路径下所有文件
        /// </summary>
        /// <param name="path"></param>
        /// <param name="rootDir"></param>
        private void AddPathRecursive(string path,string rootDir)
        {
            if(File.Exists(path))//如果是文件,则将文件加入清单
            {
                AddFile(path, rootDir);
            }
            else
            {//如果basePath是个目录
                //1.寻找目录下所有文件，压缩
                string[] paths = Directory.GetFiles(path);
                foreach (var p in paths)
                {
                    AddFile(p, rootDir);
                }
                //2.寻找目录下所有目录，递归压缩该目录                
                string[] names = Directory.GetDirectories(path);
                foreach (string p in names)
                {
                    AddPathRecursive(p, rootDir);
                }
            }
        }

        private void AddFile(string fullFile,string rootDir)
        {
            string targDir = "";//默认放在根下
            string dirName = Path.GetDirectoryName(fullFile);
            if (dirName.Contains(rootDir))
            {//如果源文件路径包含根路径，则取剩下部分作为归档路径
                targDir = dirName.Substring(rootDir.Length);
            }
            else//如果不包含，则取最后一个路径名作为归档路径
            {
                targDir = Directory.GetParent(fullFile).Name;
            }
            bFiles.Add(new AppFileInfo(fullFile, targDir));
        }
        private void BtnClear_Click(object sender, EventArgs e)
        {
            bFiles.Clear();
            dgvData.Refresh();
        }

        private void DgvData_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
        {
            if(dgvData.Columns[e.ColumnIndex].DataPropertyName=="TargetDir")
            {
                string[] paths = bFiles[e.RowIndex].SourceFile.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
                if(bFiles[e.RowIndex].TargetDir=="")
                {
                    bFiles[e.RowIndex].TargetDir = paths[paths.Length - 2];
                }
                else
                {
                    bFiles[e.RowIndex].TargetDir = "";
                }
                dgvData.Refresh();
            }
            
        }

        

        private void DgvData_CellEndEdit(object sender, DataGridViewCellEventArgs e)
        {
            if(bFiles[e.RowIndex].FileType==EFileType.MainProgram)
            {
              
                IEnumerable<AppFileInfo> selFiles = from f in bFiles
                                                   where f.FileType == EFileType.MainProgram
                                                   select f;
                foreach (var f in selFiles)
                {
                    if(f.SourceFile!=bFiles[e.RowIndex].SourceFile)
                    {
                        f.FileType = EFileType.Normal;
                    }
                }
                dgvData.Refresh();
            }
        }
        string hisTargFile = "TargFile";
        private void BtnOK_Click(object sender, EventArgs e)
        {
            string targFile = null;
            if(hisConf.ContainsKey(hisTargFile))
            {
                hisConf.ReadString(hisTargFile);
            }
            SaveFileDialog fd = new SaveFileDialog()
            {
                Title = "选择要输出的文件名",
                FileName=targFile,
                Filter = "Zip Files(*.zip)|*.zip|All files (*.*)|*.*",
                OverwritePrompt=true
            };
            if(fd.ShowDialog()==DialogResult.OK)
            {//1.在临时文件夹下生成安装配置文件 2. 把清单中的所有文件生成程序包 files.pkg 3. 加入图标、授权、配置及安装程序后制作成完整安装包
                hisConf.WriteString(hisTargFile, fd.FileName);//记忆文件名
                hisConf.Save();
                string targetPath = fd.FileName.Substring(0,fd.FileName.LastIndexOf('\\'));
                string confFullName = Path.Combine(Path.GetTempPath(), configFile);
                string pkgFile= Path.Combine(Path.GetTempPath(), pkgFilename);
                if(!GenerateConfigFile(confFullName,out string errMsg))//产生配置文件
                {
                    MessageBox.Show(errMsg,"错误",MessageBoxButtons.OK,MessageBoxIcon.Error);
                    return;
                }

                #region 打包程序文件
                if (File.Exists(pkgFile)) File.Delete(pkgFile);
                using (ZipFile zip = new ZipFile(pkgFile, Encoding.UTF8))
                {
                    foreach (var f in bFiles)
                    {
                        ZipEntry ans = zip.AddFile(f.SourceFile, f.TargetDir);
#if DEBUG
                        Debug.WriteLine($"Compressing {f.SourceFile} into {ans.FileName}");
#endif
                    }
                    zip.Save();
                    //Process.Start(fd.FileName);
                }
                #endregion

                #region 打包成安装包
                if (File.Exists(fd.FileName)) File.Delete(fd.FileName);
                using (ZipFile zip = new ZipFile(fd.FileName, Encoding.UTF8))
                {
                    ZipEntry ans = zip.AddFile(pkgFile, "");
                    ans = zip.AddFile(confFullName, "");                    
                    if (!string.IsNullOrEmpty(txtAppLogo.Text)) ans = zip.AddFile(txtAppLogo.Text, "");
                    if (!string.IsNullOrEmpty(txtLicense.Text)) ans = zip.AddFile(txtLicense.Text, "");
                    if (!string.IsNullOrEmpty(txtCLR.Text)) ans = zip.AddFile(txtCLR.Text, "");
                    if (Directory.Exists(wizardPath))
                    {
                        zip.AddDirectory(wizardPath,"");
                    }
                    else
                    {
                        MessageBox.Show("找不到安装程序！", "严重错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        return;
                    }
                    zip.Save();
                    Process.Start(Path.GetDirectoryName(fd.FileName));
                }
                #endregion
                File.Delete(pkgFile);
                File.Delete(confFullName);
                DialogResult = DialogResult.OK;
                Close();

            }

        }
        /// <summary>
        /// 以XML文件保存软件配置信息
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        private bool GenerateConfigFile(string file,out string errMsg)
        {
            errMsg = "Success";
            float size = 0;
            foreach (var f in bFiles)
            {
                FileInfo info = new FileInfo(f.SourceFile);
                size += info.Length;
            }
            var mfiles = bFiles.Where(f => f.FileType == EFileType.MainProgram);
            if(mfiles.Count()<1)
            {
                errMsg = "缺少主程序，将无法安装";
                return false;
            }
            if(mfiles.Count()>1)
            {
                errMsg = "你设置了多个主程序，我无法确定哪个是主程序，请查证。";
                return false;
            }
            AppFileInfo aFile = mfiles.First();
            var verInfo = System.Diagnostics.FileVersionInfo.GetVersionInfo(aFile.SourceFile);
           
            XmlDocument xdoc = new XmlDocument();
            var xmlsm = xdoc.CreateXmlDeclaration("1.0", "utf-8", null);
            xdoc.AppendChild(xmlsm);
            XmlElement xRoot = xdoc.CreateElement("", "InstallConfig", "");
            xdoc.AppendChild(xRoot);
            //加入APP信息
            XmlElement node = xdoc.CreateElement("AppInfo");
            node.SetAttribute("Name", verInfo.ProductName);
            node.SetAttribute("Version", verInfo.ProductVersion);
            node.SetAttribute("DotnetVersion", GetAppDotnetVersion(aFile.SourceFile));
            node.SetAttribute("Description", verInfo.Comments);
            node.SetAttribute("Size_MB", (size / (1024 * 1024)).ToString());
            node.SetAttribute("CompanyName", verInfo.CompanyName);
            node.SetAttribute("LogoFile", GetFilename(txtAppLogo.Text));
            node.SetAttribute("LicenseFile", GetFilename(txtLicense.Text));
            xRoot.AppendChild(node);

            //加入文件清单
            node = xdoc.CreateElement("Files");
            foreach (var f in bFiles)
            {
                XmlElement sn = xdoc.CreateElement("File");
                sn.SetAttribute("SourceFile", GetFilename(f.SourceFile));
                sn.SetAttribute("TargetDir", f.TargetDir);
                sn.SetAttribute("Proceed", f.Proceed.ToString());
                sn.SetAttribute("FileType", f.FileType.ToString());
                node.AppendChild(sn);
            }
            xRoot.AppendChild(node);
            xdoc.Save(file);
            return true;
        }

        private string GetFilename(string path)
        {
            if (string.IsNullOrEmpty(path)) return string.Empty;
            string[] paths = path.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
            return paths[paths.Length - 1];
        }

        string logoFile = "LogoFile";
        private void BtnBrowseLogo_Click(object sender, EventArgs e)
        {
            string file = null;
            if (hisConf.ContainsKey(logoFile)) file = hisConf.ReadString(logoFile);
            OpenFileDialog fd = new OpenFileDialog()
            {
                Filter = "pic files(*.png;*.jpg;*.bmp;*.gif)|*.png;*.jpg;*.bmp;*.gif|All files (*.*)|*.*",
                FileName = file,
                Multiselect = false
            };
            if(fd.ShowDialog()==DialogResult.OK)
            {
                hisConf.WriteString(logoFile, fd.FileName);
                txtAppLogo.Text = fd.FileName;
            }
        }
        string hisLicFile = "HisLicFile";
        private void BtnBrowseLic_Click(object sender, EventArgs e)
        {
            string file = null;
            if (hisConf.ContainsKey(hisLicFile)) file = hisConf.ReadString(hisLicFile);
            OpenFileDialog fd = new OpenFileDialog()
            {
                Filter = "license files(*.lic;*.txt)|*.lic;*.txt|All files (*.*)|*.*",
                FileName = file,
                Multiselect = false
            };
            if (fd.ShowDialog() == DialogResult.OK)
            {
                hisConf.WriteString(hisLicFile, fd.FileName);
                txtLicense.Text = fd.FileName;
            }
        }
       

        private void BtnCancel_Click(object sender, EventArgs e)
        {
            Close();
        }

        string clrFile = "ClrFile";
        private void BtnBrowseCLR_Click(object sender, EventArgs e)
        {
            string file = null;
            if (hisConf.ContainsKey(clrFile)) file = hisConf.ReadString(clrFile);
            OpenFileDialog fd = new OpenFileDialog()
            {
                Filter = "Dotnet Framework Package(*.exe)|*.exe|All files (*.*)|*.*",
                FileName = file,
                Multiselect = false
            };
            if (fd.ShowDialog() == DialogResult.OK)
            {
                hisConf.WriteString(clrFile, fd.FileName);
                txtCLR.Text = fd.FileName;
            }
        }

        private void DgvData_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
        {
            for (int i = 0; i < this.dgvData.Rows.Count;)
            {
                this.dgvData.Rows[i].DefaultCellStyle.BackColor = System.Drawing.Color.LightSkyBlue;
                i += 2;
            }
        }
        private void DgvData_UserDeletedRow(object sender, DataGridViewRowEventArgs e)
        {
            //for (int i = 0; i < this.dgvData.Rows.Count;)
            //{
            //    this.dgvData.Rows[i].DefaultCellStyle.BackColor = System.Drawing.Color.LightSkyBlue;
            //    i += 2;
            //}
        }
        private string GetAppDotnetVersion(string appFile)
        {
            var assm = System.Reflection.Assembly.LoadFile(appFile);
            var ans = assm.GetReferencedAssemblies();//获取软件依赖项

            var cor = ans.FirstOrDefault(c => c.Name == "mscorlib");
            return cor.Version.ToString();
        }

        private void FrmMaker_FormClosing(object sender, FormClosingEventArgs e)
        {
            hisConf.Save();
        }
    }
}
